#pragma once
#include "APIUtils.h"
/// TRichTextBox
/// Copyright@Trestan Chen,Canada. 2010.
/// this code is provided as is, bugs are probable, free for any use, no responsibility accepted :-)
/// Please contact trestan88@yahoo.ca,for any questions and suggestions.
namespace rjksClient
{
	using namespace System;
	using namespace System::Collections::Generic;
	//using namespace System::Linq;
	using namespace System::Text;
	using namespace System::Windows::Forms;
	using namespace System::Drawing;
	using namespace System::Runtime::InteropServices;
	[StructLayout(LayoutKind::Sequential, Pack = 1)]
	ref struct tagPOINT{
		public: int x;
		public: int y;
	};
	public ref class MetaInfo
        {
            int charIndex;
		public: property int CharIndex
            {
                int get() { return charIndex; }
                void set(int value) { charIndex = value; }
            }

            int deltaY;
		public: property int DeltaY
            {
                int get() { return deltaY; }
                void set(int value) { deltaY = value; }
            }

            Control^ theControl;
		public: property Control^ TheControl
            {
                Control^ get() { return theControl; }
                void set(Control^ value) { theControl = value; }
            }

       public: MetaInfo(Control^ theControl)
            {
                this->theControl = theControl;
            }

        };
    public ref class TRichTextBox : RichTextBox
    {
        // The following settings can be changed to read from configuration file.
	public:
        static const int MaximumTextLength = 10000;
        static const int TextLengthToBeRemoved = 3000;
        static const int MaximumNoOfControl = 50;
        static const int NoOfControlToBeRemoved = 20;
        static const bool KeepShort = true;

        List<MetaInfo^>^ ControlList;
		//event vScrollEventHandler vScroll;
	public: TRichTextBox()
           // : base()
        {
			ControlList = gcnew List<MetaInfo^>();
            this->VScroll += gcnew EventHandler(this,&TRichTextBox::TRichTextBox_VScroll);
            this->SizeChanged += gcnew EventHandler(this,&TRichTextBox::TRichTextBox_SizeChanged);
            this->LinkClicked += gcnew System::Windows::Forms::LinkClickedEventHandler(this,&TRichTextBox::TRichTextBox_LinkClicked);
        }
	/*protected: virtual void WndProc(System::Windows::Forms::Message %msg) override
    {
		__super::WndProc(msg);
        if (msg.Msg == WM_VSCROLL)
        {
            int _m=msg.WParam.ToInt32() & 0xffff;
			int pos=msg.WParam.ToInt32() >> 16;
			if(_m==SB_THUMBPOSITION)
				MessageBox::Show(pos+"---"+_m);
        }else if (msg.Msg == WM_MOUSEWHEEL){
			int pos1=msg.LParam.ToInt32() >> 16;
			int pos2=msg.LParam.ToInt32() & 0xffff;
			MessageBox::Show(pos1+"---"+pos2);
		}

    }*/
        //Contains the initial position data of a control relative to the text content.

	public: void AddControl(Control^ oneControl)
        {
            // Obtain the initial metadata.
            MetaInfo^ one = gcnew MetaInfo(oneControl);
            __super::Controls->Add(oneControl);
            one->CharIndex = this->TextLength;
			
            one->TheControl->Location = this->GetPositionFromCharIndex(one->CharIndex);
            one->DeltaY = this->GetPositionFromCharIndex(0).Y - one->TheControl->Location.Y+30;
            ControlList->Add(one);

            //"Push" the text away from the space occupied by the control.
            do
            {
                this->AppendText(" ");
            }
            while (this->GetPositionFromCharIndex(this->TextLength).X < (oneControl->Location.X + oneControl->Width));
            RemoveSome();
            //AutoScroll();

        }

	public: void AutoScroll()
        {
            this->SelectionStart = this->TextLength - 1;
            this->ScrollToCaret();
        }

	private: void RemoveSome()
        {
            //Optional. 
            //Remove some text and control if too many, to release system resources and improve performance.
            if (!KeepShort)
            {
                return;
            }

            int texttoRemove = 0;
            int imgtoRemove = 0;
            try
            {
                if (this->TextLength > MaximumTextLength)
                {
                    texttoRemove = TextLengthToBeRemoved;
                    this->Text = this->Text->Substring(texttoRemove);
                    texttoRemove += this->Text->IndexOf("\n");
                    if (texttoRemove > TextLengthToBeRemoved)
                    {
                        this->Text = this->Text->Substring(texttoRemove - TextLengthToBeRemoved);
                    }

                    for each (MetaInfo^ oldone in ControlList)
                    {
                        if (oldone->CharIndex < texttoRemove)
                        {
                            imgtoRemove++;
                        }
                        else
                        {
                            oldone->CharIndex -= texttoRemove;
                        }
                    }

                    for (int i = 0; i < imgtoRemove; i++)
                    {
                        this->Controls[0]->~Control();
                        ControlList->RemoveAt(0);
                    }
                    //need to calculate the metadata again.
                    CalculateDelta();
                }
            }
            catch (Exception^ ex)
            {
                throw ex;
            }

            try
            {
                if (ControlList->Count > MaximumNoOfControl)
                {
                    imgtoRemove = NoOfControlToBeRemoved;
                    for (int i = 0; i < imgtoRemove; i++)
                    {
                        texttoRemove = ControlList[0]->CharIndex;
                        ControlList->RemoveAt(0);
                        this->Controls[0]->~Control();
                    }
                    this->Text = this->Text->Substring(texttoRemove);
                    for each (MetaInfo^ oldone in ControlList)
                    {
                        oldone->CharIndex -= texttoRemove;
                    }
                    //need to calculate the metadata again.
                    CalculateDelta();
                }
            }
            catch (Exception^ ex)
            {
                throw ex;
            }
        }

		private: void TRichTextBox_VScroll(System::Object^ sender, EventArgs^ e)
        {
            Point pt =Point();
			interior_ptr<Point> ipt=&pt;
            APIUtils::SendMessage(this->Handle, EM_GETSCROLLPOS, 0, ipt);
			//MessageBox::Show(pt.X+","+pt.Y);
            for each (MetaInfo^ one in ControlList)
            {
                one->TheControl->Location = Point(one->TheControl->Location.X, -pt.Y - one->DeltaY);
            }
        }

		private: void TRichTextBox_SizeChanged(System::Object^ sender, EventArgs^ e)
        {
            CalculateDelta();
        }

		private: void CalculateDelta()
        {
            for each (MetaInfo^ one in ControlList)
            {
                one->TheControl->Location = this->GetPositionFromCharIndex(one->CharIndex);
                one->DeltaY = this->GetPositionFromCharIndex(0).Y - one->TheControl->Location.Y;
            }
        }

		private: void TRichTextBox_LinkClicked(System::Object^ sender, LinkClickedEventArgs^ e)
        {
            System::Diagnostics::Process::Start(e->LinkText);
        }
    };

    ref class Utility
    {

        //Bonus. A simple check on the GIF files because some may contain "bad" data and crash the program.
	public: static bool isImageCorrupted(Image^ img)
        {
            bool itis = false;

            try
            {
                if (!ImageAnimator::CanAnimate(img))
                {
                    return itis;
                }
                int frames = img->GetFrameCount(System::Drawing::Imaging::FrameDimension::Time);
                if (frames <= 1)
                {
                    return itis;
                }
                array<Byte>^ times = img->GetPropertyItem(0x5100)->Value;
                int frame = 0;
                for (; ; )
                {
                    int dur = BitConverter::ToInt32(times, 4 * frame);
                    if (++frame >= frames) break;
                    img->SelectActiveFrame(System::Drawing::Imaging::FrameDimension::Time, frame);
                }

            }
            catch (Exception^ ex)
            {
                throw ex;
            }

            return itis;
        }
    };
}
